Data Analysis and Visualization of POI Checking-In Data
Data Analysis and Visualization of POI Checking-In Data
CS2309-2 问题求解与实践 FinalProject 心路历程
0: 开胃菜:QT 初体验 Design a GUI
这是问求英文班的最后一次小作业,要求如下:
We provide a .csv file about the statistics of checking-in data over time of a POI, which contains two columns named week and num. Design a GUI that can
provide a dialog that allows users to select the .csv file and load it from disk.
provide boxes that allow users to tune the starting and ending week for display.
draw a line chart using week as x axis and num as y axis, from the starting week to the ending week set by 2.
也就是说要实现三个功能,要能从磁盘中提取csv文件读数据,然后通过交互方框获取用户的输入值,最后通过输入值和数据作出x-y 折线图。
Version 1.0
在做小作业之前我苦苦思索,花了一天时间阅读 Qt学习之路 ,除了了解一些函数和类的基本功能外(这是当时边学边复制粘贴来的笔记 ,并没有学会怎么去运用这些去实际解决问题。上最后一次问求课的时候,同学提醒我面向CSDN编程,我恍然大悟,开始直接着手捣鼓这个小作业。
我觉得画图比较简单,于是先从第三部分做起,上CSDN搜索发现一个宝贝类:QLineSeries
。我选择了一些基本功能的代码加入我的程序:
(先修改pro文件并添加名称空间)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 QLineSeries*line=new QLineSeries (); line->setName (QString ("POI" )); line->setVisible (true ); line->setPointLabelsVisible (false ); line->setPointsVisible (true ); for (int i=front;i<=rear;i++) { line->append (i,p[i]); } QChart*chart=new QChart (); chart->setAnimationOptions (QChart::AllAnimations); chart->setLocalizeNumbers (true ); chart->addSeries (line); chart->createDefaultAxes (); chart->setTitle ("POI checking-in data" ); QChartView*chartView=new QChartView (chart); chartView->setRenderHint (QPainter::Antialiasing);
这样我们画图的设置便已经完成,在这里我是直接在main函数里写的,所以直接用mainwindow
来进行操作:
1 2 3 4 MainWindow w; w.setCentralWidget (chartView); w.resize (900 ,600 ); w.show ();
这样我们第一步大体完成,然后考虑p[]
中的数据何来,于是我搜了搜有关读取.csv文件。
这里用的最关键的是一个QFileDialog
类,来实现选择文件的对话框。原理就是我们用QString
来存储读取的文件的路径名,然后用QFile
打开,用QTextStream
来读取数据。.csv文件比较容易,一行一行读取即可,读取的数据以QString
存在,我们用一个QStringList
来写成类似坐标形式,然后用at
函数来获取右边一栏的数值,用toInt
强制转换成int
存储。
代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 QString name=QFileDialog::getOpenFileName (NULL ,"Open File" ,"/home" ,"Excel(*.csv)" ); QFile file (name) ; if (!file.open (QIODevice::ReadOnly)) qDebug ()<<"OPEN FILE FAILED!" ; QTextStream *out=new QTextStream (&file); QStringList tempOption=out->readAll ().split ('\n' ); for (int i=1 ;i<tempOption.count ();i++) { n++; QStringList tt=tempOption.at (i-1 ).split ("," ); int tmp=tt.at (1 ).toInt (); p[i-1 ]=tmp; }
这样以来第二步也完成了。最后我们要获取输入值,我搜了搜,发现一个很方便的类QInputDialog
,可以直接满足我们的需求,只需强制存储我们输入的值即可,代码如下:
1 2 3 bool isOK;int front=QInputDialog::getText (NULL ,"Input Dialog :" ,"The starting week:" ,QLineEdit::Normal,"" ,&isOK).toInt ();int rear=QInputDialog::getText (NULL ,"Input Dialog :" ,"The starting week:" ,QLineEdit::Normal,"" ,&isOK).toInt ();
至此我们已经完成全部功能,全部在main
函数里实现的,main.cpp 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include "mainwindow.h" #include <QApplication> #include <QtCharts/QChartView> #include <QtCharts/QLineSeries> #include <QDoubleSpinBox> #include <QFileDialog> #include <QFile> #include <QDebug> #include <QString> #include <QInputDialog> QT_CHARTS_USE_NAMESPACE int p[100 ]={0 };int n=0 ;int main (int argc, char *argv[]) { QApplication a (argc, argv) ; MainWindow w; QString name=QFileDialog::getOpenFileName (NULL ,"Open File" ,"/home" ,"Excel(*.csv)" ); QFile file (name) ; if (!file.open (QIODevice::ReadOnly)) qDebug ()<<"OPEN FILE FAILED!" ; QTextStream *out=new QTextStream (&file); QStringList tempOption=out->readAll ().split ('\n' ); for (int i=1 ;i<tempOption.count ();i++) { n++; QStringList tt=tempOption.at (i-1 ).split ("," ); int tmp=tt.at (1 ).toInt (); p[i-1 ]=tmp; } bool isOK; int front=QInputDialog::getText (NULL ,"Input Dialog :" ,"The starting week:" ,QLineEdit::Normal,"" ,&isOK).toInt (); int rear=QInputDialog::getText (NULL ,"Input Dialog :" ,"The starting week:" ,QLineEdit::Normal,"" ,&isOK).toInt (); QLineSeries*line=new QLineSeries (); line->setName (QString ("POI" )); line->setVisible (true ); line->setPointLabelsVisible (false ); line->setPointsVisible (true ); for (int i=front;i<=rear;i++) { line->append (i,p[i]); } QChart*chart=new QChart (); chart->setAnimationOptions (QChart::AllAnimations); chart->setLocalizeNumbers (true ); chart->addSeries (line); chart->createDefaultAxes (); chart->setTitle ("POI checking-in data" ); chart->legend ()->setVisible (true ); chart->legend ()->setAlignment (Qt::AlignBottom); chart->legend ()->setBackgroundVisible (true ); chart->legend ()->setLabelColor (QColor (255 ,128 ,255 )); chart->legend ()->setVisible (true ); QFont font = chart->legend ()->font (); QChartView*chartView=new QChartView (chart); chartView->setRenderHint (QPainter::Antialiasing); w.setCentralWidget (chartView); w.resize (900 ,600 ); w.show (); return a.exec (); }
Version 2.0
但是,我转念一想,第二个要求上课的时候小姐姐讲了很长时间如何自己设计构件,我觉得这样直接用QInputDialog
太不负责任了,于是我打算推倒重建,结合上课所讲的知识来完成任务。
首先我打算把代码全部从main
里面撤销,也不用mainwindow
,而是自己定义一个widget
来实现功能,我一开始其实只是想用widget来实现自定义输入的box,后来发现这样的话在不同文件间传输数据很麻烦,且不能满足各个功能的时序性。
我在将box的样式构建好之后,应该如何触发画图呢?思来想去发现应该在main
函数里调用QObject::connect
函数,但是直接调用会报错,为什么呢? 发现这里的button
是在widget
函数里调用的,而不是widget
类,所以我们要把里面的一些小构建写在widget.h
头文件的类里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 QLabel*label=new QLabel ("Please input the beginning week:" ); QLabel*labelbel=new QLabel ("Please input the ending week:" ); spinbox=new QSpinBox (this ); spinboxbox=new QSpinBox (this ); QSlider*slider=new QSlider (Qt::Horizontal,this ); QSlider*sliderder=new QSlider (Qt::Horizontal,this ); spinbox->setRange (1 ,52 ); spinboxbox->setRange (1 ,52 ); slider->setRange (1 ,52 ); sliderder->setRange (1 ,52 ); QObject::connect (slider,&QSlider::valueChanged,spinbox,&QSpinBox::setValue); void (QSpinBox:: *spinBoxSignal)(int )=&QSpinBox::valueChanged;QObject::connect (spinbox,spinBoxSignal,slider,&QSlider::setValue); QObject::connect (sliderder,&QSlider::valueChanged,spinboxbox,&QSpinBox::setValue); QObject::connect (spinboxbox,spinBoxSignal,sliderder,&QSlider::setValue); QVBoxLayout*layout=new QVBoxLayout; btn=new QPushButton ("I have entered the value!" ); layout->addWidget (label); layout->addWidget (spinbox); layout->addWidget (slider); layout->addWidget (labelbel); layout->addWidget (spinboxbox); layout->addWidget (sliderder); layout->addWidget (btn); this ->setLayout (layout);this ->show ();
而在main
函数里,我们如此调用:
1 2 3 4 5 6 7 8 int main (int argc, char *argv[]) { QApplication a (argc, argv) ; widget w; QObject::connect (w.btn,&QPushButton::clicked,&w,&widget::showline); QObject::connect (w.btn,&QPushButton::clicked,&w,&widget::close); return a.exec (); }
也就是在外层操控,一旦按钮被点开,直接用main
函数来关闭窗口并运行画图函数widget::showline()
。我们也只能无奈地把showline
写在widget
里了。
其他的部分改动不大,我们把数组也写进了类的头文件里,把读取文件的代码块直接移植到``widget`里,于是我们改进版大功告成。(改bug改了五个小时一直到ddl前半小时才提交……)
最后放一下最终的widget.cpp
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 #include "mainwindow.h" #include "widget.h" #include <QApplication> #include <QStringList> #include <QTextStream> #include <QtDebug> #include <QChart> #include <QSlider> #include <QSpinBox> #include <QHBoxLayout> #include <QWidget> #include <QLabel> #include <QWidget> #include <QLineSeries> #include <QChartView> #include <QPushButton> QT_CHARTS_USE_NAMESPACE widget::widget (QWidget *parent) : QWidget(parent){ QString name=QFileDialog::getOpenFileName (NULL ,"Open File" ,"/home" ,"Excel(*.csv)" ); QFile file (name) ; if (!file.open (QIODevice::ReadOnly)) qDebug ()<<"OPEN FILE FAILED!" ; QTextStream *out=new QTextStream (&file); QStringList tempOption=out->readAll ().split ('\n' ); for (int i=1 ;i<tempOption.count ();i++) { n++; QStringList tt=tempOption.at (i-1 ).split ("," ); int tmp=tt.at (1 ).toInt (); p[i-1 ]=tmp; } QLabel*label=new QLabel ("Please input the beginning week:" ); QLabel*labelbel=new QLabel ("Please input the ending week:" ); spinbox=new QSpinBox (this ); spinboxbox=new QSpinBox (this ); QSlider*slider=new QSlider (Qt::Horizontal,this ); QSlider*sliderder=new QSlider (Qt::Horizontal,this ); spinbox->setRange (1 ,52 ); spinboxbox->setRange (1 ,52 ); slider->setRange (1 ,52 ); sliderder->setRange (1 ,52 ); QObject::connect (slider,&QSlider::valueChanged,spinbox,&QSpinBox::setValue); void (QSpinBox:: *spinBoxSignal)(int )=&QSpinBox::valueChanged; QObject::connect (spinbox,spinBoxSignal,slider,&QSlider::setValue); QObject::connect (sliderder,&QSlider::valueChanged,spinboxbox,&QSpinBox::setValue); QObject::connect (spinboxbox,spinBoxSignal,sliderder,&QSlider::setValue); QVBoxLayout*layout=new QVBoxLayout; btn=new QPushButton ("I have entered the value!" ); layout->addWidget (label); layout->addWidget (spinbox); layout->addWidget (slider); layout->addWidget (labelbel); layout->addWidget (spinboxbox); layout->addWidget (sliderder); layout->addWidget (btn); this ->setLayout (layout); this ->show (); } void widget::showline () { QLineSeries*line=new QLineSeries (); line->setName (QString ("POI" )); line->setVisible (true ); line->setPointLabelsVisible (false ); line->setPointsVisible (true ); for (int i=spinbox->value ();i<=spinboxbox->value ();i++) { line->append (i,p[i]); } QChart*chart=new QChart (); chart->setAnimationOptions (QChart::AllAnimations); chart->setLocalizeNumbers (true ); chart->addSeries (line); chart->createDefaultAxes (); chart->setTitle ("POI checking-in data" ); chart->legend ()->setVisible (true ); chart->legend ()->setAlignment (Qt::AlignBottom); chart->legend ()->setBackgroundVisible (true ); chart->legend ()->setLabelColor (QColor (255 ,128 ,255 )); chart->legend ()->setVisible (true ); QFont font = chart->legend ()->font (); QChartView*chartView=new QChartView (chart); chartView->setRenderHint (QPainter::Antialiasing); chartView->resize (900 ,600 ); chartView->show (); }
“小作业”艰难完成。
1:Qt 多线程的学习
由于大作业需要处理的数据量十分庞大,所以为了减少界面的卡顿,换而言之防止程序直接卡死,我们需要采用多线程的方式去优化我们的project.
本节参照bilibili上的一个教程 而画大饼大致学习完成。
线程类 QThread
常用的共用成员函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 QThread::QThread (QObject *parent = Q_NULLPTR); bool QThread::isFinished () const ;bool QThread::isRunning () const ;Priority QThread::priority () const ;void QThread::setPriority (Priority priority) ;优先级: QThread::IdlePriority --> 最低的优先级 QThread::LowestPriority QThread::LowPriority QThread::NormalPriority QThread::HighPriority QThread::HighestPriority QThread::TimeCriticalPriority --> 最高的优先级 QThread::InheritPriority --> 子线程和其父线程的优先级相同, 默认是这个 void QThread::exit (int returnCode = 0 ) ;bool QThread::wait (unsigned long time = ULONG_MAX) ;
信号槽:
1 2 3 4 5 6 7 8 9 10 11 12 13 [slot] void QThread::quit () ; [slot] void QThread::start (Priority priority = InheritPriority) ; [slot] void QThread::terminate () ; [signal] void QThread::finished () ; [signal] void QThread::started () ;
静态函数:
1 2 3 4 5 6 7 8 [static ] QThread *QThread::currentThread () ; [static ] int QThread::idealThreadCount () ; [static ] void QThread::msleep (unsigned long msecs) ; [static ] void QThread::sleep (unsigned long secs) ; [static ] void QThread::usleep (unsigned long usecs) ;
任务处理函数:
1 2 [virtual protected ] void QThread::run () ;
使用方式一:到线程搞
我们需要创建一个线程类的子类,继承QT中的线程类QThread
。
我们需要这个线程处理的任务,写在父类的run()
函数里面。重写父类的run()
,编写子线程要处理的具体的业务流程。
在主线程里我们直接创建子线程的对象,用指针new一个出来,然后调用start()
就可以直接启动了。(start
相当于是间接启动run()
)
父子线程之间的通信可以通过信号槽的方式。
看起来有手就行,写起来手呢?看一个示例 :
首先我们需要捋顺mainwindow.cpp
的逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include "mainwindow.h" #include "ui_mainwindow.h" #include "mythread.h" MainWindow::MainWindow (QWidget *parent) : QMainWindow (parent) , ui (new Ui::MainWindow) { ui->setupUi (this ); Generate* gen = new Generate; BubbleSort* bubble = new BubbleSort; QuickSort* quick = new QuickSort; connect (this , &MainWindow::starting, gen, &Generate::recvNum); connect (ui->start, &QPushButton::clicked, this , [=]() { emit starting (10000 ); gen->start (); }); connect (gen, &Generate::sendArray, bubble, &BubbleSort::recvArray); connect (gen, &Generate::sendArray, quick, &QuickSort::recvArray); connect (gen, &Generate::sendArray, this , [=](QVector<int > list){ bubble->start (); quick->start (); for (int i=0 ; i<list.size (); ++i) { ui->randList->addItem (QString::number (list.at (i))); } }); connect (bubble, &BubbleSort::finish, this , [=](QVector<int > list){ for (int i=0 ; i<list.size (); ++i) { ui->bubbleList->addItem (QString::number (list.at (i))); } }); connect (quick, &QuickSort::finish, this , [=](QVector<int > list){ for (int i=0 ; i<list.size (); ++i) { ui->quickList->addItem (QString::number (list.at (i))); } }); connect (this , &MainWindow::destroy, this , [=]() { gen->quit (); gen->wait (); gen->deleteLater (); bubble->quit (); bubble->wait (); bubble->deleteLater (); quick->quit (); quick->wait (); quick->deleteLater (); }); } MainWindow::~MainWindow () { delete ui; }
而分别的线程我们是如何实现的呢?看线程类的cpp函数,注意观察不同的类传递不同的信号:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 #include "mythread.h" #include <QElapsedTimer> #include <QDebug> Generate::Generate (QObject *parent) : QThread (parent) { } void Generate::recvNum (int num) { m_num = num; } void Generate::run () { qDebug () << "生成随机数的线程的线程地址: " << QThread::currentThread (); QVector<int > list; QElapsedTimer time; time.start (); for (int i=0 ; i<m_num; ++i) { list.push_back (qrand () % 100000 ); } int milsec = time.elapsed (); qDebug () << "生成" << m_num << "个随机数总共用时:" << milsec << "毫秒" ; emit sendArray (list) ; } BubbleSort::BubbleSort (QObject *parent) : QThread (parent) { } void BubbleSort::recvArray (QVector<int > list) { m_list = list; } void BubbleSort::run () { qDebug () << "冒泡排序的线程的线程地址: " << QThread::currentThread (); QElapsedTimer time; time.start (); int temp; for (int i=0 ; i<m_list.size (); ++i) { for (int j=0 ; j<m_list.size ()-i-1 ; ++j) { if (m_list[j] > m_list[j+1 ]) { temp = m_list[j]; m_list[j] = m_list[j+1 ]; m_list[j+1 ] = temp; } } } int milsec = time.elapsed (); qDebug () << "冒泡排序用时" << milsec << "毫秒" ; emit finish (m_list) ; } QuickSort::QuickSort (QObject *parent) : QThread (parent) { } void QuickSort::recvArray (QVector<int > list) { m_list = list; } void QuickSort::run () { qDebug () << "快速排序的线程的线程地址: " << QThread::currentThread (); QElapsedTimer time; time.start (); quickSort (m_list, 0 , m_list.size ()-1 ); int milsec = time.elapsed (); qDebug () << "快速排序用时" << milsec << "毫秒" ; emit finish (m_list) ; } void QuickSort::quickSort (QVector<int > &s, int l, int r) { if (l < r) { int i = l, j = r; int x = s[l]; while (i < j) { while (i < j && s[j] >= x) { j--; } if (i < j) { s[i++] = s[j]; } while (i < j && s[i] < x) { i++; } if (i < j) { s[j--] = s[i]; } } s[i] = x; quickSort (s, l, i - 1 ); quickSort (s, i + 1 , r); } }
以上是多线程的第一种实现方式,直接用QThread
写类,简单粗暴。
使用方式二:搞到线程
Qt 提供的第二种线程的创建方式弥补了第一种方式的缺点,用起来更加灵活,但是这种方式写起来会相对复杂一些,我们看一看具体操作:
首先我们创建一个继承QObject
的类,这个类后面我们用来执行第一种方式QThread
类中run()
函数所做的事情。
在类里添加一个公共的成员函数,函数体就是我们要子线程中致性的业务逻辑。
1 2 3 4 5 6 7 class MyWork :public QObject{ public : ....... void working () ; }
在主线程中创建一个QThread
对象,此即子线程的对象。在主线程中创建工作的类对象(注:不要给创建的对象指定父对象)。
将工作类对象移动到创建的子线程对象中,需要调用QObject
类的moveToThread()
方法。
启动子线程,调用工作函数,这时候线程便启动了,但是移动到线程中的工作对象并没有工作,这时我们调用工作对象的工作函数,让这个函数开始执行,这时候便在移动到的那个子线程中运行。
与方式一的代码功能相同,我们用方式二也实现一个:
首先理解这个代码,先看mainwindow.cpp
文件的代码逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #include "mainwindow.h" #include "ui_mainwindow.h" #include "mythread.h" #include <QThread> MainWindow::MainWindow (QWidget *parent) : QMainWindow (parent) , ui (new Ui::MainWindow) { ui->setupUi (this ); QThread* t1 = new QThread; QThread* t2 = new QThread; QThread* t3 = new QThread; Generate* gen = new Generate; BubbleSort* bubble = new BubbleSort; QuickSort* quick = new QuickSort; gen->moveToThread (t1); bubble->moveToThread (t2); quick->moveToThread (t3); connect (this , &MainWindow::starting, gen, &Generate::working); connect (ui->start, &QPushButton::clicked, this , [=]() { emit starting (10000 ); t1->start (); }); connect (gen, &Generate::sendArray, bubble, &BubbleSort::working); connect (gen, &Generate::sendArray, quick, &QuickSort::working); connect (gen, &Generate::sendArray, this , [=](QVector<int > list){ t2->start (); t3->start (); for (int i=0 ; i<list.size (); ++i) { ui->randList->addItem (QString::number (list.at (i))); } }); connect (bubble, &BubbleSort::finish, this , [=](QVector<int > list){ for (int i=0 ; i<list.size (); ++i) { ui->bubbleList->addItem (QString::number (list.at (i))); } }); connect (quick, &QuickSort::finish, this , [=](QVector<int > list){ for (int i=0 ; i<list.size (); ++i) { ui->quickList->addItem (QString::number (list.at (i))); } }); connect (this , &MainWindow::destroy, this , [=]() { t1->quit (); t1->wait (); t1->deleteLater (); t2->quit (); t2->wait (); t2->deleteLater (); t3->quit (); t3->wait (); t3->deleteLater (); gen->deleteLater (); bubble->deleteLater (); quick->deleteLater (); }); } MainWindow::~MainWindow () { delete ui; }
可以看到我们先开线程后各自工作,然后信号传输还是如方式一,不过细化到具体的工作类更加直观。
再来看一下工作类是如何实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 #include "mythread.h" #include <QElapsedTimer> #include <QDebug> #include <QThread> Generate::Generate (QObject *parent) : QObject (parent) { } void Generate::working (int num) { qDebug () << "生成随机数的线程的线程地址: " << QThread::currentThread (); QVector<int > list; QElapsedTimer time; time.start (); for (int i=0 ; i<num; ++i) { list.push_back (qrand () % 100000 ); } int milsec = time.elapsed (); qDebug () << "生成" << num << "个随机数总共用时:" << milsec << "毫秒" ; emit sendArray (list) ; } BubbleSort::BubbleSort (QObject *parent) : QObject (parent) { } void BubbleSort::working (QVector<int > list) { qDebug () << "冒泡排序的线程的线程地址: " << QThread::currentThread (); QElapsedTimer time; time.start (); int temp; for (int i=0 ; i<list.size (); ++i) { for (int j=0 ; j<list.size ()-i-1 ; ++j) { if (list[j] > list[j+1 ]) { temp = list[j]; list[j] = list[j+1 ]; list[j+1 ] = temp; } } } int milsec = time.elapsed (); qDebug () << "冒泡排序用时" << milsec << "毫秒" ; emit finish (list) ; } QuickSort::QuickSort (QObject *parent) : QObject (parent) { } void QuickSort::working (QVector<int > list) { qDebug () << "快速排序的线程的线程地址: " << QThread::currentThread (); QElapsedTimer time; time.start (); quickSort (list, 0 , list.size ()-1 ); int milsec = time.elapsed (); qDebug () << "快速排序用时" << milsec << "毫秒" ; emit finish (list) ; } void QuickSort::quickSort (QVector<int > &s, int l, int r) { if (l < r) { int i = l, j = r; int x = s[l]; while (i < j) { while (i < j && s[j] >= x) { j--; } if (i < j) { s[i++] = s[j]; } while (i < j && s[i] < x) { i++; } if (i < j) { s[j--] = s[i]; } } s[i] = x; quickSort (s, l, i - 1 ); quickSort (s, i + 1 , r); } }
内容与方式一类似,注意继承的父类是不同的,这只是单纯工作的类!
使用这种多线程方式,假设有多个不相关的业务流程需要被处理,那么就可以创建多个类似于 MyWork 的类,将业务流程放多类的公共成员函数中,然后将这个业务类的实例对象移动到对应的子线程中 moveToThread() 就可以了,这样可以让编写的程序更加灵活,可读性更强,更易于维护。